package model.symbol;

import editor.NotImplementedException;
import model.code.GlobalVariable;

/**
 * Instances of this class represent assembly language directives.
 *
 * @author Neil Dickson
 */
public abstract class Directive extends Keyword {
	protected Directive(String keyword, String description) {
		super(keyword, description);
	}

	public static final int	UNARY_MINUS_PRECEDENCE		= 8;
	public static final int	MULTIPLICATION_PRECEDENCE	= 7;
	public static final int	ADDITION_PRECEDENCE			= 6;

	public abstract int precedence();

	public static final BinaryDirective	OR = new BinaryDirective("or","Bitwise Inclusive OR Directive: \"immediate or immediate\"") {
		public long value(long left,long right) {
			return left | right;
		}
		public int precedence(){return 2;}
	};
	public static final BinaryDirective	AND = new BinaryDirective("and","Bitwise AND Directive: \"immediate and immediate\"") {
		public long value(long left,long right) {
			return left & right;
		}
		public int precedence(){return 3;}
	};
	public static final BinaryDirective	XOR = new BinaryDirective("xor","Bitwise Exclusive OR Directive: \"immediate xor immediate\"") {
		public long value(long left,long right) {
			return left ^ right;
		}
		public int precedence(){return 2;}
	};
	public static final BinaryDirective	MOD = new BinaryDirective("mod","Modulus (Division Remainder) Directive: \"immediate mod immediate\"") {
		public long value(long left,long right) {
			// TODO: Determine whether the directive follows the correct convention for "mod" (always positive remainder) or Java's convention for "mod" (difference after truncation), since in the former case, this must be adjusted for negatives
			return left % right;
		}
		public int precedence(){return MULTIPLICATION_PRECEDENCE;}
	};
	public static final BinaryDirective	SHL = new BinaryDirective("shl","Bitwise Shift Left Directive: \"immediate shl immediate\"") {
		public long value(long left,long right) {
			return left << right;
		}
		public int precedence(){return MULTIPLICATION_PRECEDENCE;}
	};
	public static final BinaryDirective	SHR = new BinaryDirective("shr","Bitwise Shift Right Directive: \"immediate shr immediate\"") {
		public long value(long left,long right) {
			return left >>> right;
		}
		public int precedence(){return MULTIPLICATION_PRECEDENCE;}
	};
	public static final UnaryDirective	NOT = new UnaryDirective("not","Bitwise NOT Directive: \"not immediate\"") {
		public long value(long right) {
			return ~right;
		}
		public int precedence(){return UNARY_MINUS_PRECEDENCE;}	// This is supposedly 4 in MASM, but then it doesn't work to do "-NOT 3" and other things with multiple unary directives
	};
	public static final UnaryDirective	LOW = new UnaryDirective("low","Extract Bits 0-7 Directive: \"low immediate\"") {
		public long value(long right) {
			return right&0xFF;
		}
		public int precedence(){return 9;}
	};
	public static final UnaryDirective	HIGH = new UnaryDirective("high","Extract Bits 8-15 Directive: \"high immediate\"") {
		public long value(long right) {
			return (right>>>8)&0xFF;
		}
		public int precedence(){return 9;}
	};
	public static final UnaryDirective	LOWWORD = new UnaryDirective("lowword","Extract Bits 0-15 Directive: \"lowword immediate\"") {
		public long value(long right) {
			return right&0xFFFF;
		}
		public int precedence(){return 9;}
	};
	public static final UnaryDirective	HIGHWORD = new UnaryDirective("highword","Extract Bits 16-31 Directive: \"highword immediate\"") {
		public long value(long right) {
			return (right>>>16)&0xFFFF;
		}
		public int precedence(){return 9;}
	};
	public static final UnaryDirective	LOG2 = new UnaryDirective("log2","Base 2 logarithm directive: \"log2(immediate)\"") {
		public long value(long right) {
			long highestBitSet = -1;
			for (int i=0;i<64;++i) {
				if ((right&(1L<<i))!=0) {
					highestBitSet = i;
				}
			}
			return highestBitSet;
		}
		public int precedence(){return UNARY_MINUS_PRECEDENCE;}
	};
	/**
	 * Unary minus is a special case: it is not a directive, but is a placeholder for minus while performing the shunting algorithm.
	 */
	public static final UnaryDirective	UNARY_MINUS = new UnaryDirective("-","Unary Minus Operator: \"-immediate\"") {
		public long value(long right) {
			return -right;
		}
		public int precedence(){return UNARY_MINUS_PRECEDENCE;}
	};
	public static final UnaryDirective	SIZEOF = new UnaryDirective("sizeof","SizeOf Directive: \"sizeof dataType\" or \"sizeof variable\"") {
		public long value(DataType right) {
			return right.getSize();
		}
		public long value(GlobalVariable right) {
			throw new NotImplementedException();
		}
		public long value(long value) {
			// NOTE: This should not be called!
			return value;
		}
		public int precedence(){return 13;}
	};
	public static final UnaryDirective	OFFSET = new UnaryDirective("offset","Offset Directive: \"offset dataType.structureMember\" or \"offset variable\" or \"offset function\" or \"offset lineLabel\"") {
		public long value(GlobalVariable right) {
			throw new NotImplementedException();
		}
		public long value(StructureMember right) {
			throw new NotImplementedException();
		}
		public long value(long value) {
			// NOTE: This should not be called!
			return value;
		}
		public int precedence(){return 10;}
	};
	public static final BinaryDirective	EQ = new BinaryDirective("eq","Equality Check Directive: \"immediate eq immediate\"") {
		public long value(long left,long right) {
			return (left == right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};
	public static final BinaryDirective	NE = new BinaryDirective("ne","Inequality Check Directive: \"immediate ne immediate\"") {
		public long value(long left,long right) {
			return (left != right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};
	public static final BinaryDirective	LT = new BinaryDirective("lt","Less Than Check Directive: \"immediate lt immediate\"") {
		public long value(long left,long right) {
			return (left < right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};
	public static final BinaryDirective	GT = new BinaryDirective("gt","Greater Than Check Directive: \"immediate gt immediate\"") {
		public long value(long left,long right) {
			return (left > right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};
	public static final BinaryDirective	LE = new BinaryDirective("le","Less Than Or Equal Check Directive: \"immediate le immediate\"") {
		public long value(long left,long right) {
			return (left <= right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};
	public static final BinaryDirective	GE = new BinaryDirective("ge","Greater Than Or Equal Check Directive: \"immediate ge immediate\"") {
		public long value(long left,long right) {
			return (left >= right) ? -1 : 0;
		}
		public int precedence(){return 5;}
	};

	/**
	 * Instances of this class represent assembly language directives that accept 2 operands, left and right.
	 */
	public static abstract class BinaryDirective extends Directive {
		protected BinaryDirective(String keyword, String description) {
			super(keyword, description);
		}
		public abstract long value(long left,long right);
	}

	/**
	 * Instances of this class represent assembly language directives that accept 1 operand, on the right.
	 */
	public static abstract class UnaryDirective extends Directive {
		protected UnaryDirective(String keyword, String description) {
			super(keyword, description);
		}
		public abstract long value(long right);
	}
}
